package com.imflea.zero.aop;

import com.github.jaemon.dinger.DingerSender;
import com.github.jaemon.dinger.core.entity.DingerRequest;
import com.github.jaemon.dinger.core.entity.enums.MessageSubType;
import com.imflea.zero.exception.BizException;
import com.imflea.zero.util.ZeroCacheUtils;
import com.imflea.zero.util.ZeroJsonUtils;
import com.imflea.zero.util.base.BaseDateUtils;
import com.imflea.zero.util.base.CommonUtils;
import com.imflea.zero.utils.SessionUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;


@Component
@Aspect
@Slf4j
public class FarmAop {
    @Autowired
    private DingerSender dingerSender;


    @Value("${spring.application.name}")
    private String appName;

    /**
     * 类切点
     */
    @Pointcut(value = "execution(* com.imflea.zero.api..*+.*(..)) "
            + "|| execution(* com.imflea.zero.service..*+.*(..)) "
            + "|| execution(* com.imflea.zero.controller..*.*(..)) "
    )

    public void farmAspect() {
    }

    /**
     * @param pdj 切入点
     * @history 修订历史（历次修订内容、修订人、修订时间等）
     */
    @Around("farmAspect()")
    public Object aroundMethod(ProceedingJoinPoint pdj) throws Exception {
        final String tranceId = SessionUtils.getTranceId();
        final String parentSessionId = SessionUtils.getParentSessionId();
        final String sessionId = SessionUtils.getSessionId();
        String parentServiceName = "";
        String serviceName = "";
        String reqData = "";
        Object resData = "";
        Long startTime = 0L;
        Long endTime = 0L;
        Long takeTime = 0L;
        String exception = "";
        Object result = "";
        try {
            startTime = System.currentTimeMillis();
            final Object[] paramValues = pdj.getArgs();
            for (Object obj : paramValues) {
                if (!CommonUtils.isNull(obj)) {
                    reqData += obj.toString() + ";";
                } else {
                    reqData += ";";
                }
            }
            final Signature signature = pdj.getSignature();
            final String lj = signature.getDeclaringTypeName();
            final String functionname = signature.getName();
            serviceName = lj + "." + functionname;
            SessionUtils.setAopStackParentServiceName(serviceName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            result = pdj.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            final String ex = ZeroCacheUtils.getString("String:Exception:" + sessionId);
            if (CommonUtils.isNull(ex)) {
                final StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw, true));
                exception = sw.toString();
                ZeroCacheUtils.setString("String:Exception:" + sessionId, exception);
                ZeroCacheUtils.expire("String:Exception:" + sessionId, 600);
                throw new BizException(e.getMessage());
            } else {
                exception = ex;
                throw new BizException(e.getMessage());
            }
        } finally {
            if (!CommonUtils.isNull(result)) {
                resData = result.toString();
            }
            parentServiceName = SessionUtils.getAopStackParentServiceName();
            endTime = System.currentTimeMillis();
            final String userId = SessionUtils.getUserId();
            final String userName = SessionUtils.getUserName();
            final Map<String, Object> map = new HashMap<String, Object>();
            takeTime = endTime - startTime;
            final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            map.put("system_key", "log-system");
            map.put("app_name", appName);
            map.put("trance_id", tranceId);
            map.put("session_id", sessionId);
            map.put("parent_session_id", parentSessionId);
            map.put("service_name", serviceName);
            map.put("parent_service_name", parentServiceName);
            map.put("request_data", reqData);
            map.put("response_data", resData);
            map.put("start_time", sf.format(startTime));
            map.put("end_time", sf.format(endTime));
            map.put("take_time", takeTime);
            map.put("user_id", userId);
            map.put("user_name", userName);
            map.put("take_date", BaseDateUtils.getSystemCurrentTime("yyyy-MM-dd HH:mm:ss"));
            map.put("exception", exception);
            map.put("uuid", CommonUtils.generateRandomString());
            map.put("server_ip", CommonUtils.getLocalHostLANAddress());
            if (!CommonUtils.isNull(exception)) {
                dingerSender.send(MessageSubType.TEXT, DingerRequest.request("异常信息监控：" + ZeroJsonUtils.mapToJson(map), "监控"));
            }
            log.info(ZeroJsonUtils.mapToJson(map));
        }
        return result;
    }
}
